;		DIRF.MAC (Z80 VERSION) ver 1.3
;	          by Keith Petersen, W8SDZ
;		     (revised 12/19/80)
;
;      DIRECTORY FUNCTION FOR CP/M 1.4 OR 2.x PROGRAMS
;
;--->NOTE: Assemble with Microsoft M80 assembler
;
;This file contains routines which can be included in any
;CP/M program to allow listing the directory.  No sorting
;is done because that would require use of more memory above
;the program.  These routines use the 80h default buffer for
;all operations so if you have data there be sure to move it
;before running this directory function.  Assume all registers
;destroyed when calling 'DIRF'.
;
;This module may be assembled as a stand-alone program for
;testing.  L80 link command for testing:   L80 DIRF,DIRF,/N/E
;Execute program from CP/M, not L80.  It will return to the
;CCP after finishing.
;
	.Z80		   ;Z80 SWITCH FOR ASSEMBLER
	ENTRY	DIRF	   ;DEFINE DIRF AS PUBLIC
;
NPL	EQU	4	   ;NUMBER OF NAMES PER LINE
TAB	EQU	9	   ;HORIZONTAL TAB
CR	EQU	0DH	   ;CARRIAGE RETURN
LF	EQU	0AH	   ;LINE FEED
;
;BDOS Equates
;
RDCHR	EQU	1	   ;READ CHAR FROM CONSOLE
WRCHR	EQU	2	   ;WRITE CHR TO CONSOLE
PRINT	EQU	9	   ;PRINT CONSOLE BUFF
CONST	EQU	11	   ;CHECK CONS STAT
FSRCHF	EQU	17	   ;0FFH=NOT FOUND
FSRCHN	EQU	18	   ;   "       "
CURDSK	EQU	25	   ;GET CURRENT DISK NAME
BDOS	EQU	5
FCB	EQU	5CH
;
;First, we preserve old stack pointer and set a new one
;because some functions take more stack space than may
;be available in the calling program.
DIRF:	LD	HL,0
	ADD	HL,SP	   ;GET OLD STACK POINTER
	LD	(STACK),HL ;SAVE FOR LATER
	LD	SP,STACK   ;SET NEW STACK POINTER
;
;Check FCB for drive request
	LD	A,(FCB)	   ;GET DRIVE NAME FROM FCB
	OR	A	   ;ANY REQUESTED? (0=NO)
	JR	NZ,GOTDRV  ;NOT ZERO MEANS WE HAVE NAME
;
;Get drive name
	LD	C,CURDSK   ;GET CURRENT DRIVE NAME
	CALL	BDOS
	INC	A	   ;MAKE 'A' RELATIVE TO 1 NOT 0
;
;Print signon message and drive name
GOTDRV:	ADD	A,40H	   ;MAKE IT ASCII
	LD	(DNAME),A  ;SAVE IT IN MESSAGE
	LD	DE,MSG	   ;POINT TO MESSAGE
	LD	C,PRINT	   ;PRINT IT
	CALL	BDOS
;
;Make FCB all '?' to match any file
	LD	HL,FCB+1
	LD	B,11	   ;FN+FT COUNT
;
QLOOP:	LD	(HL),'?'   ;STORE '?' IN FCB
	INC	HL
	DEC	B
	JR	NZ,QLOOP
;
;Initialize number of names per line counter
	LD	A,NPL	   ;NR. NAMES PER LINE
	LD	(NNAMS),A  ;INIT COUNTER
;
;Look up the FCB in the directory
	LD	C,FSRCHF   ;GET 'SEARCH FIRST' FNC
	LD	DE,FCB
	CALL	BDOS	   ;READ FIRST
	INC	A	   ;WERE THERE ANY?
	JR	NZ,SOME	   ;GOT SOME
	CALL	ERXIT
	DEFB	'++NO FILE$'
;
;Read more directory entries
MORDIR:	LD	C,FSRCHN   ;SEARCH NEXT
	LD	DE,FCB
	CALL	BDOS	   ;READ DIR ENTRY
	INC	A	   ;CHECK FOR END (0FFH)
	JP	Z,EXIT	   ;NO MORE - EXIT
;
;Point to directory entry
SOME:	DEC	A	   ;UNDO PREV 'INC A'
	AND	3	   ;MAKE MODULUS 4
	ADD	A,A	   ;MULTIPLY...
	ADD	A,A	   ;..BY 32 BECAUSE
	ADD	A,A	   ;..EACH DIRECTORY
	ADD	A,A	   ;..ENTRY IS 32
	ADD	A,A	   ;..BYTES LONG
	LD	HL,81H	   ;POINT TO BUFFER (SKIP TO FN/FT)
	ADD	A,L	   ;POINT TO ENTRY
	LD	L,A	   ;SAVE - HL NOW = ENTRY ADRS
;
;Check for console break
	PUSH	HL	   ;SAVE NAME POINTER
	LD	C,CONST	   ;CK STATUS OF KBD
	CALL	BDOS
	POP	HL	   ;RESTORE NAME POINTER
	OR	A	   ;ANY KEY PRESSED?
	JP	NZ,ABORT   ;YES, ABORT
;
;Print an entry
	LD	B,8	   ;FILE NAME LENGTH
	CALL	TYPEIT	   ;TYPE FILENAME
	LD	A,'.'	   ;PERIOD AFTER FN
	CALL	TYPE
	LD	B,3	   ;GET THE FILETYPE
	CALL	TYPEIT
	LD	HL,NNAMS   ;POINT TO NAMES COUNTER
	DEC	(HL)	   ;ONE LESS ON THIS LINE
	PUSH	AF
	CALL	NZ,FENCE   ;NO CR-LF NEEDED, DO FENCE
	POP	AF
	CALL	Z,CRLF	   ;CR-LF NEEDED
	JP	MORDIR
;
;Print two spaces, fence character, then two more spaces
FENCE:	CALL	TWOSPC
	LD	A,':'	   ;FENCE CHARACTER
	CALL	TYPE
;
;Print two spaces
TWOSPC:	CALL	SPACE
;
;Print one space
SPACE:	LD	A,' '
;
;Type char in A register
TYPE:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	E,A	   ;CHAR TO E FOR CP/M
	LD	C,WRCHR	   ;WRITE CHAR TO CONSOLE FUNC
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	RET	
;
;Type (B) characters from memory (HL)
TYPEIT:	LD	A,(HL)
	AND	7FH	   ;REMOVE CP/M 2.x ATTRIBUTES
	CALL	TYPE
	INC	HL
	DEC	B
	JR	NZ,TYPEIT
	RET	
;
;CR-LF routine. HL=NNAMS upon entry
CRLF:	LD	A,CR	   ;CR
	CALL	TYPE
	LD	A,LF	   ;LF
	CALL	TYPE
	LD	(HL),NPL   ;NUMBER OF NAMES PER LINE
	RET	
;
;Error exit
ERXIT:	POP	DE	   ;GET MSG
	LD	C,PRINT
	JR	CALLB	   ;PRINT MSG, EXIT
;
;Abort - read char entered to clear it from CP/M buffer
ABORT:	LD	C,RDCHR	   ;DELETE THE CHAR
;
;Fall into CALLB
;
CALLB:	CALL	BDOS
;
;Fall into EXIT
;
;Exit - All done, return to caller
EXIT:	LD	HL,(STACK) ;GET OLD STACK
	LD	SP,HL	   ;MOVE TO STACK
	RET		   ;...AND RETURN
;
MSG:	DEFB	TAB,TAB,'    Directory for drive '
DNAME:	DEFB	'X:',CR,LF,'$'
;
;Temporary storage area
;
NNAMS:	DEFS	1	   ;NAMES PER LINE COUNTER
	DEFS	40	   ;ROOM FOR STACK
STACK:	DEFS	2	   ;OLD STACK STORED HERE
;
;END OF DIRECTORY MODULE
;
;Temporary end statement used only for testing DIRF module.
;Remove before including module in other programs.
	END	DIRF
